#include <stdio.h>
#include <stdlib.h>
#include "sikle.h"

void init_sikel_state(SikleState* ss, Lexer* lex)
{
  ss->lexer = lex;
  ss->program_stack = ProgramStack_new(0x2000);
  ss->uname_program_stack = ProgramStack_new(0x1000);
  ss->exec_stack = ExecStack_new(0x1000);
  ss->word_table = HashTable_new();

  register_builtin_words(ss);
}

static void word_decl(SikleState* ss)
{
  int count = 0;
  Word word;

  // printf("word_decl\n");

  next_token(LEX(ss));
  word.type = WT_SIKLE;
  word.name = ss_string(ss);
  word.entry = PS(ss)->sp;

  next_token(LEX(ss));
  while(ss_tok_type(ss) != TOK_COLON) {
    push_token(PS(ss), ss_tok(ss));
    count++;
    next_token(LEX(ss));
  }
  word.count = count;

  HashTable_put_word(WT(ss), word);

  // printf("word_decl_end\n");
}

void uname_word_decl(SikleState* ss)
{
  int count = 0;
  Word* word = (Word*)malloc(sizeof(Word));

  word->entry = UPS(ss)->sp;

  next_token(LEX(ss));

  while(ss_tok_type(ss) != TOK_RBRACKET) {
    push_token(UPS(ss), ss_tok(ss));
    count++;
    next_token(LEX(ss));
  }
  word->count = count;

  /* push uname word to exec stack */
  push_ptr(ss, word);
}

void call_name_word(SikleState* ss, NString name);

void call_word(SikleState* ss, Word* word)
{
  int count = 0;
  Token* token_pc = NULL;

  token_pc = (Token*)word->entry;
  count = word->count;
  while(count--) {
    switch(token_pc->type) {
    case TOK_INTEGER:
      // printf("push INTEGER(%d)\n", tok_integer(*token_pc));
      push_integer(ss, tok_integer(*token_pc));
      break;
    case TOK_COLON:
      printf("ERROR: colon in word decl\n");
      exit(0);
    case TOK_LBRACKET:
      printf("ERROR: [] in word decl not support yet\n");
      exit(0);
      break;
    case TOK_STRING:
      // printf("push STRING(%s)\n", tok_string(*token_pc).data);
      push_string(ss, tok_string(*token_pc));
      break;
    case TOK_WORD:
      call_name_word(ss, tok_string(*token_pc));
      break;
    default:
      break;
    }
    token_pc++;
  }
}

void call_name_word(SikleState* ss, NString name)
{
  Word* word = NULL;

  //  printf("call_word\n");
  word = HashTable_get_word(WT(ss), name);

  if(word == NULL) {
    printf("ERROR: WORD(\"%s\") is not defined.\n", name.data);
    exit(0);
  }

  if(word->type == WT_BUILTIN) {
    ((builtin_word_func)word->entry)(ss);
  }
  else if(word->type == WT_SIKLE) {  
    call_word(ss, word);
  }
}

const char* const token_string[] = {
  "integer",
  ":",
  "string",
  "word",
  "[",
  "]",
  "eos"
};

void parser(SikleState* ss)
{
  next_token(LEX(ss));
  
  for(;;) {
    switch(ss_tok_type(ss)) {
    case TOK_INTEGER:
      // printf("push INTEGER(%d)\n", ss_integer(ss));
      push_integer(ss, ss_integer(ss));
      break;
    case TOK_COLON:
      word_decl(ss);
      break;
    case TOK_LBRACKET:
      uname_word_decl(ss);
      break;
    case TOK_STRING:
      // printf("push STRING(%s)\n", ss_string(ss).data);
      push_string(ss, ss_string(ss));
      break;
    case TOK_WORD:
      call_name_word(ss, ss_string(ss));
      break;
    case TOK_EOS:
      return;
    default:
      printf("ERROR: not expect %s\n", token_string[ss_tok_type(ss)]);
      break;
    }
    next_token(LEX(ss));
  }  
}

void sikle(char* filename)
{
  Lexer lex;
  SikleState ss;

  init_lexer(&lex, filename);
  init_sikel_state(&ss, &lex);
  
  parser(&ss);
}


int main(int argc, char** argv)
{
  if(argc != 2){
    printf("Usage: sikle filename\n");
    exit(1);
  }
  
  sikle(argv[1]);
    
  return 0;
}
